Add capability reliability ledger (#186) - #326
Draft
Joncallim wants to merge 2 commits into
Draft
Conversation
Issue #186 is the next unblocked child of Epic #184: #201 (deterministic operation catalog) and #185 (canonical execution outcomes) have both landed, and the Epic's own recommended order puts the reliability ledger next. This is an architecture-only handoff for a Backend specialist. No implementation, migration, or schema change is included. Key decisions: - Reliability is stored as individual append-only capability attempts keyed to a cohort fingerprint over project, capability, scope, runtime/model, and policy. Requalification is implicit: a material change produces a different cohort rather than silently reusing an old sample count. - Attempts are immutable. Verification results, human decisions, rollback, override, and detected evidence drift are appended as separate adjudication rows so later evidence never rewrites what was recorded at the time. - verification_mode decides what counts as verified. self_reported and human_review never contribute to the independently verified pass rate, and independent_agent is refused at ingest until #188 can produce it. The gap is reported as an explicit unverified-completion rate instead of being folded into a pass rate. - The ledger has no free-text column at all. Every text column is a closed enum, a 64-hex fingerprint, or the bounded capability-key grammar, so prose, paths, and credentials cannot enter it even by accident. - Metrics are a pure function of stored evidence with no materialized cache. Insufficient samples and drifted evidence fail closed to explicit states, and critical failures are always reported regardless of the aggregate. The document also pins the four migration-count gates and the closed application-ACL inventory an implementer must update alongside migration 0031, and lists the stop conditions where they must escalate instead of improvising. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TP6Smtka3KKMZKKv6G5cJz
9 tasks
Implements the architecture in docs/architecture/issue-186-capability- reliability-ledger.md and ADR 0012. Adds two new immutable tables, capability_attempts and capability_attempt_adjudications, that record comparable evidence for how reliably a specific capability has performed in a specific project/scope/runtime/policy cohort -- without collapsing materially different work into one score and without letting a worker grade its own output. - lib/reliability/contracts.ts: the versioned contract -- capability-key grammar, cohort/scope/runtime/policy fingerprints (domain-separated SHA-256, matching the ADR 0011 operation-fingerprint construction), verification modes, and the ReliabilitySummary shape. - lib/reliability/metrics.ts: computeReliability, a pure function with no clock or I/O. Below a minimum sample size it reports insufficient_evidence with null rates; if any in-window attempt's linked outcome has changed since ingest it reports evidence_drift and withholds all rates; critical failures are always counted regardless of state. - db/migrations/0031_capability_reliability_ledger.sql: both tables are append-only via BEFORE UPDATE/DELETE reject triggers, adjudications enforce gapless per-attempt sequencing via a BEFORE INSERT guard, and every text column is closed by a CHECK -- no free-text column exists in the ledger. - worker/reliability/ledger.ts + context.ts: idempotent ingest keyed on (execution_outcome_id, capability_key), wired best-effort into the three work-package outcome boundaries, the ADR 0011 operation-finalize path (after its transaction commits, never inside it), and review-gate decisions as append-only adjudications. independent_agent verification is refused at ingest since #188 has no producer for it yet. - worker/reliability/reader.ts + scripts/inspect-capability-reliability.ts: read-only cohort lookup and a CLI inspection command; no dashboard or HTTP route in this slice. - CI: extends the closed application-ACL inventory so the ordinary app role gets exactly SELECT/INSERT on both new tables, and updates the four pinned migration-count/timestamp literals for migration 0031. upsertExecutionOutcome now returns the stored row id so ingest can link to it; existing callers that ignored the previous void return are unaffected. Rollback and override adjudications have storage, metrics, and tests but no producer until #189/#190 exist to emit them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TP6Smtka3KKMZKKv6G5cJz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Source Issue
Closes #186
Parent Epic: #184
Agent Run
Runtime: claude-code
Run ID: n/a
Summary
This PR now contains both the architecture and the full implementation of #186 — Add capability reliability ledger, per
docs/architecture/issue-186-capability-reliability-ledger.mdand ADR 0012.Two new immutable tables —
capability_attemptsandcapability_attempt_adjudications— record comparable evidence for how reliably a specific capability has performed in a specific project/scope/runtime/policy cohort, without collapsing materially different work into one score and without letting a worker grade its own output.Contracts (
lib/reliability/contracts.ts): capability-key grammar (workpackage:<role>/<capability>oroperation:<id>@<version>), domain-separated SHA-256 cohort/scope/runtime/policy fingerprints matching the ADR 0011 construction, closedverification_modeenum, and theReliabilitySummaryshape.Metrics (
lib/reliability/metrics.ts):computeReliabilityis a pure function — no clock, no I/O. Below a minimum sample size it reportsinsufficient_evidencewith null rates; if any in-window attempt's linked outcome has changed since ingest it reportsevidence_driftand withholds all rates; critical failures are always counted regardless of state. No materialized cache in v1 — deliberately, see §6.3 of the design doc.Persistence (
db/migrations/0031_capability_reliability_ledger.sql): both tables are append-only viaBEFORE UPDATE/DELETEreject triggers; adjudications enforce gapless per-attempt sequencing via aBEFORE INSERTguard; everytextcolumn is closed by aCHECK— there is no free-text column anywhere in the ledger, so a model's prose, a file path, or a secret cannot enter it even by accident.Ingest (
worker/reliability/ledger.ts+context.ts): idempotent on(execution_outcome_id, capability_key), wired best-effort into the three existing work-package outcome boundaries (admission block, completion, failure), the ADR 0011 operation-finalize path (after its transaction commits, never inside it), and review-gate decisions as append-only adjudications.independent_agentverification is refused at ingest since #188 has no producer for it yet — the honest current state (completed, but nobody independent checked) is reported as an explicitunverifiedCompletionrate instead.Read path (
worker/reliability/reader.ts) and CLI (scripts/inspect-capability-reliability.ts→npm run protocol:inspect-capability-reliability): read-only cohort lookup with drift detection against the linkedexecution_outcomesrow. No dashboard, no HTTP route in this slice — that's #191.CI: extends the closed application-ACL inventory so the ordinary app role gets exactly
SELECT, INSERTon both new tables (noUPDATE, noDELETE), and updates the four pinned migration-count/timestamp literals for migration0031(31→32 migrations, new max timestamp).One non-additive change:
upsertExecutionOutcomenow returns the stored row id so ingest can link to it. Existing callers that ignored the previousvoidreturn are unaffected.Acceptance Criteria Validation
Full mapping in §14 of the design doc. Two are explicitly partial rather than claimed complete:
execution_outcome_id NOT NULLON CONFLICT DO NOTHING, proven in both the mocked and gated-postgres suitesself_reported/human_reviewnever count towardindependentlyVerifiedPassdecideReviewGate; rollback/override have storage, metrics, and tests but no producer until [FEATURE] Add evidence-based earned autonomy policy engine #189/[FEATURE] Add Project Sentinel detection and escalation flow #190 exist to emit themTests / Verification
npm run test:unit:zero-skip— full suite green except one pre-existing, unrelated flake (epic-172-s6-runner-failure-injection.test.ts, a process-kill/deadline test that fails identically onmainbefore this change — confirmed viagit stash).capability-reliability-contracts.test.ts,capability-reliability-metrics.test.ts(20 tests, pure-function/fingerprint coverage),capability-reliability-ledger.test.ts(mocked-DB ingest: idempotency target,independent_agentrefusal, flag-off, missing/overflow classification, multiplicity),capability-reliability-schema.test.ts(migration text assertions + CI ACL gate assertion),capability-reliability-ledger.postgres.test.ts(gated proof of append-only triggers, gapless sequencing, duplicate-insert rejection, and role privileges — mirrorsoperation-ledger.postgres.test.ts, requiresFORGE_RELIABILITY_LEDGER_REQUIRE_POSTGRES_TEST=1plusDATABASE_URL/admin URL to run; skips cleanly without them).review-gates.test.ts's pinneddbSelectcall count (6→8) to account for the two new best-effort adjudication lookups.npx tsc --noEmitandnpx eslint .both clean.Risks / Follow-up
both, with Security/Adversarial review applying perAGENTS.md— this slice touches durable evidence, an append-only audit boundary, and database privileges. Worth particular attention: the ACL inventory edit (.github/workflows/web-ci.yml) and the no-free-text invariant across both new tables.